From 56bd460f3d8523e84e844e8326950bf92a40f91d Mon Sep 17 00:00:00 2001 From: robertl Date: Wed, 8 Jan 2003 16:06:36 +0000 Subject: [PATCH] Completly new, data driven XSV handlers. Thanx, Alex! --- gpsbabel/Makefile | 5 +- gpsbabel/README | 38 +++ gpsbabel/README.xmapwpt | 66 ++++ gpsbabel/csv_util.c | 598 ++++++++++++++++++++++++++++++++- gpsbabel/csv_util.h | 86 +++++ gpsbabel/defs.h | 1 + gpsbabel/mxf.c | 212 ++++-------- gpsbabel/ozi.c | 284 +++++----------- gpsbabel/reference/xmapwpt.wpt | 9 + gpsbabel/style/README.style | 254 ++++++++++++++ gpsbabel/style/csv.style | 22 ++ gpsbabel/style/custom.style | 48 +++ gpsbabel/style/dna.style | 24 ++ gpsbabel/style/gpsdrive.style | 20 ++ gpsbabel/style/gpsman.style | 29 ++ gpsbabel/style/mxf.style | 34 ++ gpsbabel/style/nima.style | 42 +++ gpsbabel/style/ozi.style | 42 +++ gpsbabel/style/s_and_t.style | 33 ++ gpsbabel/style/tiger.style | 23 ++ gpsbabel/style/xmap.style | 24 ++ gpsbabel/testo | 21 ++ gpsbabel/vecs.c | 14 + gpsbabel/xcsv.c | 383 +++++++++++++++++++++ gpsbabel/xmapwpt.c | 115 +++++++ 25 files changed, 2061 insertions(+), 366 deletions(-) create mode 100644 gpsbabel/README.xmapwpt create mode 100644 gpsbabel/reference/xmapwpt.wpt create mode 100644 gpsbabel/style/README.style create mode 100644 gpsbabel/style/csv.style create mode 100644 gpsbabel/style/custom.style create mode 100644 gpsbabel/style/dna.style create mode 100644 gpsbabel/style/gpsdrive.style create mode 100644 gpsbabel/style/gpsman.style create mode 100644 gpsbabel/style/mxf.style create mode 100644 gpsbabel/style/nima.style create mode 100644 gpsbabel/style/ozi.style create mode 100644 gpsbabel/style/s_and_t.style create mode 100644 gpsbabel/style/tiger.style create mode 100644 gpsbabel/style/xmap.style create mode 100644 gpsbabel/xcsv.c create mode 100644 gpsbabel/xmapwpt.c diff --git a/gpsbabel/Makefile b/gpsbabel/Makefile index 6f5fb0dea..f1a549c0d 100644 --- a/gpsbabel/Makefile +++ b/gpsbabel/Makefile @@ -2,7 +2,8 @@ CFLAGS=-g -Icoldsync FMTS=magproto.o gpx.o geo.o gpsman.o mapsend.o mapsource.o \ gpsutil.o tiger.o pcx.o csv.o cetus.o gpspilot.o magnav.o \ - psp.o mxf.o holux.o garmin.o ozi.o tmpro.o dna.o tpg.o gpsdrive.o + psp.o mxf.o holux.o garmin.o ozi.o tmpro.o dna.o tpg.o gpsdrive.o \ + xcsv.o xmapwpt.o JEEPS=jeeps/gpsapp.o jeeps/gpscom.o jeeps/gpsfmt.o jeeps/gpsinput.o \ jeeps/gpsmath.o jeeps/gpsmem.o \ @@ -64,6 +65,8 @@ main.o: main.c defs.h queue.h mapsend.o: mapsend.c defs.h queue.h mapsend.h mapsource.o: mapsource.c defs.h queue.h mkshort.o: mkshort.c defs.h queue.h +xmapwpt.o: mxf.c defs.h queue.h csv_util.h +xcsv.o: xcsv.c defs.h queue.h csv_util.h mxf.o: mxf.c defs.h queue.h csv_util.h ozi.o: ozi.c defs.h queue.h csv_util.h pcx.o: pcx.c defs.h queue.h diff --git a/gpsbabel/README b/gpsbabel/README index 31b8419a8..cab201249 100644 --- a/gpsbabel/README +++ b/gpsbabel/README @@ -100,6 +100,44 @@ THE FORMATS This is the format used to hot-sync to XMap from withing TopoUSA. Done with help of Dan Edwards. + XMapWpt + + Delorme XMapHandHeld Street Atlas USA is another of the billion + CSV variants. This is the format used by XmapHH SA USA on + (at least) PocketPC O/S. Please see README.xmapwpt for more + information on it's intricacies. This XMap is not to be confused + with the XMap mentioned above. Contributed to gpsbabel by + Alex Mottram. + + XCSV + + XCSV is an open-ended "Whatever Separated Values" parser / writer + designed to work with user-supplied "style" files. It should handle + at least a few thousand of the billion CSV variants available. + By itself, it doesn't comply to any format, however *most* CSV + variants can be described as a "style" and fine-tuned by the end + user. For more information on it's use, please see README.style + in the style/ sub-directory of gpsbabel. For an example of using + the XCSV module within your C program, look at the ozi.c, mxf.c, and + xmapwpt.c sources in the gpsbabel directory. This module was + contributed to gpsbabel by Alex Mottram. + + Additional Options: + style - **REQUIRED** Path to XCSV style file. + + snlen - Maximum length of synthesized shortnames. + snwhite - Switch defining whether or not to allow whitespace + in synthesized shortnames. + (0 = NO WHITESPACE, 1 = WHITESPACE OK). + snupper - Switch defining whether or not to force uppercase + in shortnames. (0 = LEAVE AS IS, 1 = UPPERCASE ALL). + + NOTE: sn* options require use of the '-s' command line option. + + Example Usage: + gpsbabel -i xsv,style=foo.style -f foo -o xsv,style=bar.style -F bar + gpsbabel -s -i gpx -f foo.gpx -o xsv,style=my.style,snlen=8 -F bar + MAPSEND Magellan was smart enough to document their file format to make diff --git a/gpsbabel/README.xmapwpt b/gpsbabel/README.xmapwpt new file mode 100644 index 000000000..6e8539c1e --- /dev/null +++ b/gpsbabel/README.xmapwpt @@ -0,0 +1,66 @@ + +NOTE: THIS README PERTAINS TO THE "XMAPWPT" FORMAT, NOT THE XMAP/TOPO USA + 4.0 CONDUIT "XMAP" FORMAT. + + +Delorme XMap Handheld .WPT for PocketPC is a bit of a kludge. This +document covers XMap Handheld Street Atlas USA edition. + +XMap on the PocketPC stores it's waypoints in individual .wpt files. +For example, waypoints generated by XMap on the PocketPC are stored +by default in the "My Documents" folder using the sequential names +"XMap1.wpt", "XMap2.wpt", ad nauseum. Needless to say, not very +efficient. + +As writing multiple waypoint files is outside of the scope of gpsbabel, +gpsbabel chooses to write one big file, one waypoint per line. +Extracting lines from this file is left as an exercise for the end user. +A simple perl script to handle this conversion is included at the end +of this README. + +It should also be noted that READING multiple files is indeed possible, +but if you have more than a few points, it can be a task. For example: + +gpsbabel -i xmapwpt -f Xmap1.wpt -f Xmap2.wpt -o mapsend -F mapsend.wpt + +will read the two Xmap .wpt files and write one mapsend file. This +is fine for a small handful of points, but could be quite cumbersome +for folks like me who have 100+ waypoints loaded into XMap. For *nix +folks, something as simple as: + +cat *.wpt > /tmp/foo.wpt +gpsbabel -i xmapwpt -f foo.wpt -o mapsend -F mapsend.wpt + +will do the trick just fine. + + +############ BEGIN SCRIPT +#!/full/path/to/perl +$INPUTFILE = @ARGV[0]; +$TARGETDIR = @ARGV[1]; +$FILENAME = @ARGV[2]; + +if (! $FILENAME) { + print "Usage: xmap_split.pl INPUT_FILE OUTPUT_DIRECTORY FILENAME_BASE\n"; + print " (i.e. xmapl_split.pl points.wpt /tmp/points GPSB)\n"; + print " (created GPSB0001-GPSBXXXX in /tmp/points/ from points.wpt)\n"; + exit; +} + +open (INFILE, $INPUTFILE) || die "Cannot open $INPUTFILE for read!\n"; + +while () { + $lc++; + $filename = sprintf("%s/Gpsb%04d.wpt", $TARGETDIR, $lc); + + open (OUTFILE, ">$filename") || die "Cannot open $filename for write!\n"; + + print OUTFILE $_; + + close(OUTFILE); +} + +exit; + +########### END SCRIPT + diff --git a/gpsbabel/csv_util.c b/gpsbabel/csv_util.c index e7687b5a4..03fe2f880 100644 --- a/gpsbabel/csv_util.c +++ b/gpsbabel/csv_util.c @@ -1,5 +1,5 @@ /* - Utilities for parsing Comma Seperated Value files (CSV) + Utilities for parsing Character Separated Value files (CSV) Copyright (C) 2002 Alex Mottram (geo_alexm at cox-internet.com) @@ -26,6 +26,14 @@ #define MYNAME "CSV_UTIL" +/* macros */ +#define LAT_DIR(a) a < 0.0 ? 'S' : 'N' +#define LON_DIR(a) a < 0.0 ? 'W' : 'E' + +/* convert excel time (days since 1900) to time_t and back again */ +#define EXCEL_TO_TIMET(a) ((a - 25569.0) * 86400.0) +#define TIMET_TO_EXCEL(a) ((a / 86400.0) + 25569.0) + /*********************************************************************/ /* csv_stringclean() - remove any unwanted characters from string. */ /* returns copy of string. */ @@ -132,7 +140,7 @@ csv_lineparse(const char *stringstart, const char *delimited_by, const char *sp; static const char *p = NULL; static char *tmp = NULL; - size_t dlen, elen; + size_t dlen = 0, elen = 0; int enclosedepth = 0; short int dfound; @@ -155,8 +163,10 @@ csv_lineparse(const char *stringstart, const char *delimited_by, sp = p; /* length of delimiters and enclosures */ - dlen = strlen(delimited_by); - elen = strlen(enclosed_in); + if (delimited_by) + dlen = strlen(delimited_by); + if (enclosed_in) + elen = strlen(enclosed_in); dfound = 0; @@ -170,7 +180,6 @@ csv_lineparse(const char *stringstart, const char *delimited_by, if ((!enclosedepth) && (strncmp(p, delimited_by, dlen) == 0)) { dfound = 1; - } else { p++; } @@ -198,3 +207,582 @@ csv_lineparse(const char *stringstart, const char *delimited_by, return (tmp); } +/*****************************************************************************/ +/* dec_to_intdeg() - convert decimal degrees to integer degreees */ +/* usage: i = dec_to_intdeg(31.1234, 1); */ +/* i = dec_to_intdeg(91.1234, 0); */ +/*****************************************************************************/ +static int +dec_to_intdeg(const double d, const int islat) +{ + int ideg = 0; + + if (islat) { + ideg = (2147483647) - (d * 8388608); + } else { + ideg = (2147483647) - (fabs(d) * 8388608) + 1; + } + + return(ideg); +} + +/*****************************************************************************/ +/* intdeg_to_dec() - convert integer degrees to decimal degreees */ +/* usage: lat = dec_to_intdeg(ilat, 1); */ +/* lon = dec_to_intdeg(ilon, 0); */ +/*****************************************************************************/ +static double +intdeg_to_dec(const int ideg, const int islat) +{ + double d; + + if (islat) { + d = ((2147483647) - ideg) / (double)8388608; + } else { + d = ((-2147483647-1) + ideg) / (double)8388608; + } + + return(d); +} + +/*****************************************************************************/ +/* decdir_to_dec() - convert a decimal/direction value into pure decimal. */ +/* usage: lat = decdir_to_dec("W90.1234"); */ +/* lat = decdir_to_dec("30.1234N"); */ +/*****************************************************************************/ +static double +decdir_to_dec(const char * decdir) +{ + char *p; + const char *cp; + double rval; + int sign = 0; + + cp = &decdir[0]; + + if ((*cp == 'W') || (*cp == 'S')) + sign = -1; + else + if ((*cp == 'N') || (*cp == 'E')) + sign = 1; + + rval = sign ? strtod(&decdir[1], &p) : strtod(&decdir[0], &p); + + if (sign == 0) { + if ((*p == 'W') || (*p == 'S')) + sign = -1; + else + if ((*p == 'N') || (*p == 'E')) + sign = 1; + } + + return(rval * sign); +} + +/*****************************************************************************/ +/* xcsv_file_init() - prepare xcsv_file for first use. */ +/*****************************************************************************/ +void +xcsv_file_init(void) +{ + memset(&xcsv_file, '\0', sizeof(xcsv_file_t)); + + QUEUE_INIT(&xcsv_file.prologue); + QUEUE_INIT(&xcsv_file.epilogue); + + QUEUE_INIT(&xcsv_file.ifield); + /* ofield is alloced to allow pointing back at ifields + * where applicable. + */ + xcsv_file.ofield = xcalloc(sizeof(queue), 1); + QUEUE_INIT(xcsv_file.ofield); +} + +/*****************************************************************************/ +/* xcsv_ifield_add() - add input field to ifield queue. */ +/* usage: xcsv_ifield_add("DESCRIPTION", "", "%s") */ +/*****************************************************************************/ +void +xcsv_ifield_add(char *key, char *val, char *pfc) +{ + field_map_t *fmp = xcalloc(sizeof(*fmp), 1); + + fmp->key = key; + fmp->val = val; + fmp->printfc = pfc; + + ENQUEUE_TAIL(&xcsv_file.ifield, &fmp->Q); + xcsv_file.ifield_ct++; +} + +/*****************************************************************************/ +/* xcsv_ofield_add() - add output field to ofield queue. */ +/* usage: xcsv_ofield_add("LAT_DECIMAL", "", "%08.5lf") */ +/*****************************************************************************/ +void +xcsv_ofield_add(char *key, char *val, char *pfc) +{ + field_map_t *fmp = xcalloc(sizeof(*fmp), 1); + + fmp->key = key; + fmp->val = val; + fmp->printfc = pfc; + + ENQUEUE_TAIL(xcsv_file.ofield, &fmp->Q); + xcsv_file.ofield_ct++; +} + +/*****************************************************************************/ +/* xcsv_prologue_add() - add prologue line to prologue queue */ +/* usage: xcsv_prologue_add("Four score and seven years ago today,") */ +/*****************************************************************************/ +void +xcsv_prologue_add(char *prologue) +{ + ogue_t* ogp = xcalloc(sizeof(*ogp), 1); + + ogp->val = prologue; + ENQUEUE_TAIL(&xcsv_file.prologue, &ogp->Q); + xcsv_file.prologue_lines++; +} + +/*****************************************************************************/ +/* xcsv_epilogue_add() - add epilogue line to epilogue queue */ +/* usage: xcsv_epilogue_add("shall not perish from the earth.") */ +/*****************************************************************************/ +void +xcsv_epilogue_add(char *epilogue) +{ + ogue_t * ogp = xcalloc(sizeof(*ogp), 1); + + ogp->val = epilogue; + ENQUEUE_TAIL(&xcsv_file.epilogue, &ogp->Q); + xcsv_file.epilogue_lines++; +} + +/*****************************************************************************/ +/* xcsv_parse_val() - parse incoming data into the waypt structure. */ +/* usage: xcsv_parse_val("-123.34", *waypt, *field_map) */ +/*****************************************************************************/ +static void +xcsv_parse_val(const char *s, waypoint *wpt, const field_map_t *fmp) +{ + if (strcmp(fmp->key, "IGNORE") == 0) { + /* IGNORE -- Categorically ignore this... */ + } else + if (strcmp(fmp->key, "CONSTANT") == 0) { + /* CONSTANT -- Ignore on Input... */ + } else + if (strcmp(fmp->key, "ANYNAME") == 0) { + /* ANYNAME -- Ignore -- this is output magic. */ + } else + if (strcmp(fmp->key, "INDEX") == 0) { + /* IGNORE -- Calculated Sequence # For Ouput*/ + } else + if (strcmp(fmp->key, "SHORTNAME") == 0) { + wpt->shortname = csv_stringtrim(s, ""); + } else + if (strcmp(fmp->key, "DESCRIPTION") == 0) { + wpt->description = csv_stringtrim(s, ""); + } else + if (strcmp(fmp->key, "NOTES") == 0) { + wpt->notes = csv_stringtrim(s, ""); + } else + if (strcmp(fmp->key, "URL") == 0) { + wpt->url = csv_stringtrim(s, ""); + } else + if (strcmp(fmp->key, "URL_LINK_TEXT") == 0) { + wpt->url_link_text = csv_stringtrim(s, ""); + } else + if (strcmp(fmp->key, "ICON_DESCR") == 0) { + wpt->icon_descr = csv_stringtrim(s, ""); + } else + + /* LATITUDE CONVERSIONS**************************************************/ + if (strcmp(fmp->key, "LAT_DECIMAL") == 0) { + /* latitude as a pure decimal value */ + wpt->position.latitude.degrees = atof(s); + } else + if ((strcmp(fmp->key, "LAT_DECIMALDIR") == 0) || + (strcmp(fmp->key, "LAT_DIRDECIMAL") == 0)) { + /* latitude as a decimal with N/S in it. */ + wpt->position.latitude.degrees = decdir_to_dec(s); + } else + if (strcmp(fmp->key, "LAT_INT32DEG") == 0) { + /* latitude as a 32 bit integer offset */ + wpt->position.latitude.degrees = intdeg_to_dec(atof(s), 1); + } else + /* LONGITUDE CONVERSIONS ***********************************************/ + if (strcmp(fmp->key, "LON_DECIMAL") == 0) { + /* longitude as a pure decimal value */ + wpt->position.longitude.degrees = atof(s); + } else + if ((strcmp(fmp->key, "LON_DECIMALDIR") == 0) || + (strcmp(fmp->key, "LON_DIRDECIMAL") == 0)) { + /* longitude as a decimal with N/S in it. */ + wpt->position.longitude.degrees = decdir_to_dec(s); + } else + if (strcmp(fmp->key, "LON_INT32DEG") == 0) { + /* longitude as a 32 bit integer offset */ + wpt->position.longitude.degrees = intdeg_to_dec(atof(s), 0); + } else + + /* DIRECTIONS **********************************************************/ + if (strcmp(fmp->key, "LAT_DIR") == 0) { + /* latitude N/S. Ignore on input for now */ + } else + if (strcmp(fmp->key, "LON_DIR") == 0) { + /* longitude E/W. Ingore on input for now */ + } else + + /* ALTITUDE CONVERSIONS ************************************************/ + if (strcmp(fmp->key, "ALT_FEET") == 0) { + /* altitude in feet as a decimal value */ + wpt->position.altitude.altitude_meters = atof(s) * .3048; + } else + if (strcmp(fmp->key, "ALT_METERS") == 0) { + /* altitude in meters as a decimal value */ + wpt->position.altitude.altitude_meters = atof(s); + } else + + /* TIME CONVERSIONS ***************************************************/ + if (strcmp(fmp->key, "EXCEL_TIME") == 0) { + /* Time as Excel Time */ + wpt->creation_time = EXCEL_TO_TIMET(atof(s)); + } else + if (strcmp(fmp->key, "TIMET_TIME") == 0) { + /* Time as time_t */ + wpt->creation_time = atol(s); + } else + + /* GEOCACHING STUFF ***************************************************/ + if (strcmp(fmp->key, "GEOCACHE_DIFF") == 0) { + /* Geocache Difficulty as an int */ + wpt->gc_data.diff = atof(s) * 10; + } else + if (strcmp(fmp->key, "GEOCACHE_TERR") == 0) { + /* Geocache Terrain as an int */ + wpt->gc_data.terr = atof(s) * 10; + } else + if (strcmp(fmp->key, "GEOCACHE_TYPE") == 0) { + /* Geocache Type - TODO */ + /* wpt->gc_data.type = gs_mktype(s);*/ + + } else { + fprintf (stderr, "UNKNOWN STYLE DIRECTIVE: %s\n", fmp->key); + } +} + +/*****************************************************************************/ +/* xcsv_data_read() - read input file, parsing lines, fields and handling */ +/* any data conversion (the input meat) */ +/*****************************************************************************/ +void +xcsv_data_read(void) +{ + char buff[8192]; + char *s; + waypoint *wpt_tmp; + int linecount = 0; + queue *elem, *tmp; + field_map_t *fmp; + ogue_t *ogp; + + do { + linecount++; + memset(buff, '\0', sizeof(buff)); + fgets(buff, sizeof(buff), xcsv_file.xcsvfp); + + rtrim(buff); + + /* skip over x many lines on the top for the prologue... */ + if ((xcsv_file.prologue_lines) && ((linecount - 1) < + xcsv_file.prologue_lines)) { + continue; + } + + /* We should skip over epilogue lines also. Since we don't want to + * pre-read the file to know how many data lines we should be seeing, + * we take this cheap shot at the data and cross our fingers. + */ + + QUEUE_FOR_EACH(&xcsv_file.epilogue, elem, tmp) { + ogp = (ogue_t *) elem; + if (strncmp(buff, ogp->val, strlen(ogp->val)) == 0) { + buff[0] = '\0'; + break; + } + } + + if (strlen(buff)) { + wpt_tmp = xcalloc(sizeof(*wpt_tmp), 1); + + s = buff; + s = csv_lineparse(s, xcsv_file.field_delimiter, "", linecount); + + /* reset the ifield queue */ + elem = QUEUE_FIRST(&xcsv_file.ifield); + + /* now rip the line apart, advancing the queue for each tear + * off the beginning of buff since there's no index into queue. + */ + while (s) { + fmp = (field_map_t *) elem; + xcsv_parse_val(s, wpt_tmp, fmp); + + elem = QUEUE_NEXT(elem); + + if (elem == &xcsv_file.ifield) { + /* we've wrapped the queue. so stop parsing! */ + break; + } + + s = csv_lineparse(NULL, xcsv_file.field_delimiter, "", + linecount); + } + waypt_add(wpt_tmp); + } + + } while (!feof(xcsv_file.xcsvfp)); +} + +/*****************************************************************************/ +/* xcsv_waypt_pr() - write output file, handling output conversions */ +/* (the output meat) */ +/*****************************************************************************/ +static void +xcsv_waypt_pr(const waypoint *wpt) +{ + char buff[1024]; + static int index = 0; + char *shortname = NULL; + char *description = NULL; + char * anyname = NULL; + int i; + field_map_t *fmp; + queue *elem, *tmp; + + if (wpt->shortname) { + anyname = xstrdup(wpt->shortname); + } else + if (wpt->description) { + anyname = xstrdup(wpt->description); + } else + if (wpt->notes) { + anyname = xstrdup(wpt->notes); + } else + anyname = xstrdup(""); + + if ((anyname) && (global_opts.synthesize_shortnames)) { + anyname = mkshort(anyname); + } + + if ((! wpt->shortname) || (global_opts.synthesize_shortnames)) { + if (wpt->description) { + if (global_opts.synthesize_shortnames) + shortname = mkshort(wpt->description); + else + shortname = csv_stringclean(wpt->description, xcsv_file.badchars); + } else { + /* no description available */ + shortname = xstrdup(""); + } + } else{ + shortname = csv_stringclean(wpt->shortname, xcsv_file.badchars); + } + + if (! wpt->description) { + if (shortname) { + description = csv_stringclean(shortname, xcsv_file.badchars); + } else { + description = xstrdup(""); + } + } else { + description = csv_stringclean(wpt->description, xcsv_file.badchars); + } + + i = 0; + QUEUE_FOR_EACH(xcsv_file.ofield, elem, tmp) { + fmp = (field_map_t *) elem; + + if (i != 0) + fprintf (xcsv_file.xcsvfp, xcsv_file.field_delimiter); + + i++; + + if (strcmp(fmp->key, "IGNORE") == 0) { + /* IGNORE -- Write the char printf conversion */ + snprintf (buff, sizeof(buff), fmp->printfc, ""); + } else + if (strcmp(fmp->key, "INDEX") == 0) { + snprintf (buff, sizeof(buff), fmp->printfc, index + atoi(fmp->val)); + } else + if (strcmp(fmp->key, "CONSTANT") == 0) { + snprintf (buff, sizeof(buff), fmp->printfc, fmp->val); + } else + if (strcmp(fmp->key, "SHORTNAME") == 0) { + snprintf (buff, sizeof(buff), fmp->printfc, shortname); + } else + if (strcmp(fmp->key, "ANYNAME") == 0) { + snprintf (buff, sizeof(buff), fmp->printfc, anyname); + } else + if (strcmp(fmp->key, "DESCRIPTION") == 0) { + snprintf (buff, sizeof(buff), fmp->printfc, description); + } else + if (strcmp(fmp->key, "NOTES") == 0) { + snprintf (buff, sizeof(buff), fmp->printfc, wpt->notes); + } else + if (strcmp(fmp->key, "URL") == 0) { + snprintf (buff, sizeof(buff), fmp->printfc, wpt->url); + } else + if (strcmp(fmp->key, "URL_LINK_TEXT") == 0) { + snprintf (buff, sizeof(buff), fmp->printfc, wpt->url_link_text); + } else + if (strcmp(fmp->key, "ICON_DESCR") == 0) { + snprintf (buff, sizeof(buff), fmp->printfc, wpt->icon_descr); + } else + + /* LATITUDE CONVERSION***********************************************/ + if (strcmp(fmp->key, "LAT_DECIMAL") == 0) { + /* latitude as a pure decimal value */ + snprintf (buff, sizeof(buff), fmp->printfc, + wpt->position.latitude.degrees); + } else + if (strcmp(fmp->key, "LAT_DECIMALDIR") == 0) { + /* latitude as a decimal value with N/S after it */ + snprintf (buff, sizeof(buff), fmp->printfc, + fabs(wpt->position.latitude.degrees), + LAT_DIR(wpt->position.latitude.degrees)); + } else + if (strcmp(fmp->key, "LAT_DIRDECIMAL") == 0) { + /* latitude as a decimal value with N/S before it */ + snprintf (buff, sizeof(buff), fmp->printfc, + LAT_DIR(wpt->position.latitude.degrees), + fabs(wpt->position.latitude.degrees)); + } else + if (strcmp(fmp->key, "LAT_INT32DEG") == 0) { + /* latitude as an integer offset from 0 degrees */ + snprintf (buff, sizeof(buff), fmp->printfc, + dec_to_intdeg(wpt->position.latitude.degrees, 1)); + } else + + /* LONGITUDE CONVERSIONS*********************************************/ + if (strcmp(fmp->key, "LON_DECIMAL") == 0) { + /* longitude as a pure decimal value */ + snprintf (buff, sizeof(buff), fmp->printfc, + wpt->position.longitude.degrees); + } else + if (strcmp(fmp->key, "LON_DECIMALDIR") == 0) { + /* latitude as a decimal value with N/S after it */ + snprintf (buff, sizeof(buff), fmp->printfc, + fabs(wpt->position.longitude.degrees), + LON_DIR(wpt->position.longitude.degrees)); + } else + if (strcmp(fmp->key, "LON_DIRDECIMAL") == 0) { + /* latitude as a decimal value with N/S before it */ + snprintf (buff, sizeof(buff), fmp->printfc, + LON_DIR(wpt->position.longitude.degrees), + fabs(wpt->position.longitude.degrees)); + } else + if (strcmp(fmp->key, "LON_INT32DEG") == 0) { + /* longitudee as an integer offset from 0 degrees */ + snprintf (buff, sizeof(buff), fmp->printfc, + dec_to_intdeg(wpt->position.longitude.degrees, 0)); + } else + + /* DIRECTIONS *******************************************************/ + if (strcmp(fmp->key, "LAT_DIR") == 0) { + /* latitude N/S as a char */ + snprintf (buff, sizeof(buff), fmp->printfc, + LAT_DIR(wpt->position.latitude.degrees)); + } else + if (strcmp(fmp->key, "LON_DIR") == 0) { + /* longitude E/W as a char */ + snprintf (buff, sizeof(buff), fmp->printfc, + LON_DIR(wpt->position.longitude.degrees)); + } else + + /* ALTITUDE CONVERSIONS**********************************************/ + if (strcmp(fmp->key, "ALT_FEET") == 0) { + /* altitude in feet as a decimal value */ + snprintf (buff, sizeof(buff), fmp->printfc, + (wpt->position.altitude.altitude_meters * 3.2808)); + } else + if (strcmp(fmp->key, "ALT_METERS") == 0) { + /* altitude in meters as a decimal value */ + snprintf (buff, sizeof(buff), fmp->printfc, + wpt->position.altitude.altitude_meters); + } else + + /* TIME CONVERSIONS**************************************************/ + if (strcmp(fmp->key, "EXCEL_TIME") == 0) { + /* creation time as an excel (double) time */ + snprintf (buff, sizeof(buff), fmp->printfc, + TIMET_TO_EXCEL(wpt->creation_time)); + } else + if (strcmp(fmp->key, "TIMET_TIME") == 0) { + /* time as a time_t variable */ + snprintf (buff, sizeof(buff), fmp->printfc, wpt->creation_time); + } else + + /* GEOCACHE STUFF **************************************************/ + if (strcmp(fmp->key, "GEOCACHE_DIFF") == 0) { + /* Geocache Difficulty as a double */ + snprintf (buff, sizeof(buff), fmp->printfc, + wpt->gc_data.diff / 10); + } else + if (strcmp(fmp->key, "GEOCACHE_TERR") == 0) { + /* Geocache Terrain as a double */ + snprintf (buff, sizeof(buff), fmp->printfc, + wpt->gc_data.terr / 10); + } else + if (strcmp(fmp->key, "GEOCACHE_TYPE") == 0) { + /* Geocache Type TODO */ + snprintf (buff, sizeof(buff), fmp->printfc, "Geocache"); + } else { + /* this should probably never happen */ + } + + fprintf (xcsv_file.xcsvfp, "%s", buff); + + } + + fprintf (xcsv_file.xcsvfp, "%s", xcsv_file.record_delimiter); + + if (shortname) + free(shortname); + + if (description) + free(description); + + if (anyname) + free(anyname); + + index++; +} + +/*****************************************************************************/ +/* xcsv_data_write(void) - write prologues, spawn the output loop, and write */ +/* epilogues. */ +/*****************************************************************************/ +void +xcsv_data_write(void) +{ + queue *elem, *tmp; + ogue_t *ogp; + + /* output prologue lines, if any. */ + QUEUE_FOR_EACH(&xcsv_file.prologue, elem, tmp) { + ogp = (ogue_t *) elem; + fprintf (xcsv_file.xcsvfp, "%s%s", ogp->val, xcsv_file.record_delimiter); + } + + waypt_disp_all(xcsv_waypt_pr); + + /* output epilogue lines, if any. */ + QUEUE_FOR_EACH(&xcsv_file.epilogue, elem, tmp) { + ogp = (ogue_t *) elem; + fprintf (xcsv_file.xcsvfp, "%s%s", ogp->val, xcsv_file.record_delimiter); + } +} + diff --git a/gpsbabel/csv_util.h b/gpsbabel/csv_util.h index 5d2660bf8..2fdffed32 100644 --- a/gpsbabel/csv_util.h +++ b/gpsbabel/csv_util.h @@ -18,6 +18,7 @@ */ /* function prototypes */ + char * csv_stringtrim(const char *string, const char *enclosure); @@ -26,3 +27,88 @@ csv_lineparse(const char *stringstart, const char *delimited_by, const char *enc char * csv_stringclean(const char *string, const char *chararray); + +void +xcsv_data_read(void); + +void +xcsv_data_write(void); + +void +xcsv_file_init(void); + +void +xcsv_prologue_add(char *); + +void +xcsv_epilogue_add(char *); + +void +xcsv_ifield_add(char *, char *, char *); + +void +xcsv_ofield_add(char *, char *, char *); + +void +xcsv_destroy_style(void); + +/****************************************************************************/ +/* types required for various xcsv functions */ +/****************************************************************************/ + +/* something to map fields to waypts */ +typedef struct field_map { + queue Q; + char * key; + char * val; + char * printfc; +} field_map_t; + +/* a queuing struct for prologues / epilogues */ +typedef struct ogue { + queue Q; + char * val; +} ogue_t; + +/* something to map config file constants to chars */ +typedef struct char_map { + const char * key; + const char * chars; +} char_map_t; + +/* + * a type describing all the wonderful elements of xcsv files, in a + * nutshell. + */ +typedef struct { + int is_internal; /* bool - is internal (1) or parsed (0) */ + + int prologue_lines; /* # of lines to ignore at top of the file */ + int epilogue_lines; /* # of lines to ignore at bottom of file */ + + /* header lines for writing at the top of the file. */ + queue prologue; + + /* footer lines for writing at the bottom of the file. */ + queue epilogue; + + char * field_delimiter; /* comma, quote, etc... */ + char * record_delimiter; /* newline, c/r, etc... */ + + char * badchars; /* characters we never write to output */ + + queue ifield; /* input field mapping */ + queue * ofield; /* output field mapping */ + + int ifield_ct; /* actual # of ifields */ + int ofield_ct; /* actual # of ofields */ + + FILE * xcsvfp; /* ptr to current *open* data file */ + +} xcsv_file_t; + + +/****************************************************************************/ +/* obligatory global struct */ +/****************************************************************************/ +xcsv_file_t xcsv_file; diff --git a/gpsbabel/defs.h b/gpsbabel/defs.h index 264ad482f..d7c6f3186 100644 --- a/gpsbabel/defs.h +++ b/gpsbabel/defs.h @@ -212,6 +212,7 @@ void *xcalloc(size_t nmemb, size_t size); void *xmalloc(size_t size); char *xstrdup(const char *s); void rtrim(char *s); +signed int get_tz_offset(void); /* * PalmOS records like fixed-point numbers, which should be rounded diff --git a/gpsbabel/mxf.c b/gpsbabel/mxf.c index db20ccb13..6a90375b5 100644 --- a/gpsbabel/mxf.c +++ b/gpsbabel/mxf.c @@ -30,177 +30,85 @@ #include "defs.h" #include "csv_util.h" -#include #define MYNAME "MXF" -static FILE *file_in; -static FILE *file_out; - -static void -rd_init(const char *fname, const char *args) +static void +mxf_set_style() { - file_in = fopen(fname, "r"); - if (file_in == NULL) { - fatal(MYNAME ": Cannot open %s for reading\n", fname); + /* set up the csv xcsv_file struct */ + xcsv_file_init(); + + /* this is an internal style, don't mess with it */ + xcsv_file.is_internal = 1; + + /* how the file gets split up */ + xcsv_file.field_delimiter = ", "; + xcsv_file.record_delimiter = "\n"; + xcsv_file.badchars = "\","; + + xcsv_ifield_add("LAT_DECIMAL", "", "%08.5f"); + xcsv_ifield_add("LON_DECIMAL", "", "%08.5f"); + xcsv_ifield_add("DESCRIPTION", "", "%s"); + xcsv_ifield_add("SHORTNAME", "", "%s"); + xcsv_ifield_add("IGNORE", "", "%s"); + xcsv_ifield_add("CONSTANT", "ff0000", "%s"); + xcsv_ifield_add("CONSTANT", "47", "%s"); + + xcsv_ofield_add("LAT_DECIMAL", "", "%08.5f"); + xcsv_ofield_add("LON_DECIMAL", "", "%08.5f"); + xcsv_ofield_add("DESCRIPTION", "", "\"%s\""); + xcsv_ofield_add("SHORTNAME", "", "\"%s\""); + xcsv_ofield_add("DESCRIPTION", "", "\"%s\""); + xcsv_ofield_add("CONSTANT", "ff0000", "%s"); + xcsv_ofield_add("CONSTANT", "47", "%s"); + + /* set up mkshort */ + if (global_opts.synthesize_shortnames) { + setshort_length(32); + setshort_whitespace_ok(0); + setshort_badchars(xcsv_file.badchars); } } -static void -rd_deinit(void) +static void +mxf_rd_init(const char *fname, const char *args) { - fclose(file_in); -} + mxf_set_style(); -static void -wr_init(const char *fname, const char *args) -{ - file_out = fopen(fname, "w"); - if (file_out == NULL) { - fatal(MYNAME ": Cannot open %s for writing\n", fname); + xcsv_file.xcsvfp = fopen(fname, "r"); + + if (xcsv_file.xcsvfp == NULL) { + fatal(MYNAME ": Cannot open %s for reading\n", fname ); } } -static void -wr_deinit(void) +static void +mxf_wr_init(const char *fname, const char *args) { - fclose(file_out); -} + mxf_set_style(); -static void -data_read(void) -{ - char buff[1024]; - char *s; - waypoint *wpt_tmp; - int i; - int linecount = 0; - - do { - linecount++; - memset(buff, '\0', sizeof(buff)); - fgets(buff, sizeof(buff), file_in); - - if (strlen(buff)) { - - wpt_tmp = xcalloc(sizeof(*wpt_tmp), 1); - - /* data delimited by commas, possibly enclosed in quotes. */ - s = buff; - - s = csv_lineparse(s, ",", "\"", linecount); - - i = 0; - while (s) { - switch (i) { - case 0: - wpt_tmp->position.latitude.degrees = atof(s); - break; - case 1: - wpt_tmp->position.longitude.degrees = atof(s); - break; - case 2: - wpt_tmp->description = csv_stringtrim(s, ""); - break; - case 3: - wpt_tmp->shortname = csv_stringtrim(s, ""); - break; - case 4: - /* ignore. another name-type */ - break; - case 5: - /* ignore: color */ - break; - case 6: - /* ignore: icon */ - break; - default: - /* whoa! nelly */ - fprintf (stderr, "%s: Warning: data fields on line %d exceed specification.\n", - MYNAME, linecount); - break; - } - i++; - - s = csv_lineparse(NULL, ",", "\"", linecount); - } - - if (i != 7) { - free(wpt_tmp); - fprintf (stderr, "%s: WARNING - extracted %d fields from line %d. \nData on line ignored.\n", - MYNAME, i, linecount); - } else { - waypt_add(wpt_tmp); - } - - } else { - /* empty line */ - } - - } while (!feof(file_in)); -} - -static void -mxf_waypt_pr(const waypoint * wpt) -{ - int icon = 47; /* default to "dot" */ - const char *color_hex = "ff0000"; - char *shortname = NULL; - char *description = NULL; - - if ((! wpt->shortname) || (global_opts.synthesize_shortnames)) { - if (wpt->description) { - if (global_opts.synthesize_shortnames) - shortname = mkshort(wpt->description); - else - shortname = csv_stringclean(wpt->description, ",\""); - } else { - /* no description available */ - shortname = xstrdup(""); - } - } else{ - shortname = csv_stringclean(wpt->shortname, ",\""); + xcsv_file.xcsvfp = fopen(fname, "w"); + + if (xcsv_file.xcsvfp == NULL) { + fatal(MYNAME ": Cannot open %s for reading\n", fname ); } - - if (! wpt->description) { - if (shortname) { - description = csv_stringclean(shortname, ",\""); - } else { - description = xstrdup(""); - } - } else{ - description = csv_stringclean(wpt->description, ",\""); - } - - fprintf(file_out, "%08.5f, %08.5f, \"%s\", \"%s\", \"%s\", %s, %d\n", - wpt->position.latitude.degrees, wpt->position.longitude.degrees, - description, shortname, description, - color_hex, icon); - - if (description) - free(description); - if (shortname) - free(shortname); } -static void -data_write(void) +static void +mxf_deinit(void) { - if (global_opts.synthesize_shortnames) { - setshort_length(32); - setshort_whitespace_ok(0); - setshort_badchars("\","); - } - - waypt_disp_all(mxf_waypt_pr); + if (xcsv_file.xcsvfp) + fclose(xcsv_file.xcsvfp); + + xcsv_destroy_style(); } ff_vecs_t mxf_vecs = { - rd_init, - wr_init, - rd_deinit, - wr_deinit, - data_read, - data_write, + mxf_rd_init, + mxf_wr_init, + mxf_deinit, + mxf_deinit, + xcsv_data_read, + xcsv_data_write, }; - diff --git a/gpsbabel/ozi.c b/gpsbabel/ozi.c index b04d05dac..9d3e8bc5b 100644 --- a/gpsbabel/ozi.c +++ b/gpsbabel/ozi.c @@ -27,234 +27,102 @@ #include "defs.h" #include "csv_util.h" -#include -#include /* for floor */ #define MYNAME "OZI" -static FILE *file_in; -static FILE *file_out; - -static void -rd_init(const char *fname, const char *args) +static void +ozi_set_style() { - file_in = fopen(fname, "r"); - if (file_in == NULL) { - fatal(MYNAME ": Cannot open %s for reading\n", fname); + /* set up the ozi xcsv_file struct */ + xcsv_file_init(); + + /* this is an internal style, don't mess with it */ + xcsv_file.is_internal = 1; + + /* how the file gets split up */ + xcsv_file.field_delimiter = ","; + xcsv_file.record_delimiter = "\n"; + xcsv_file.badchars = ","; + + /* prologue */ + xcsv_prologue_add("OziExplorer Waypoint File Version 1.1"); + xcsv_prologue_add("WGS 84"); + xcsv_prologue_add("Reserved 2"); + xcsv_prologue_add("Reserved 3"); + + /* individual field mappings */ + xcsv_ifield_add("INDEX", "1", "%4d"); + xcsv_ifield_add("SHORTNAME", "", "%-14.14s"); + xcsv_ifield_add("LAT_DECIMAL", "", "%11.6f"); + xcsv_ifield_add("LON_DECIMAL", "", "%11.6f"); + xcsv_ifield_add("EXCEL_TIME", "", "%011.5f"); + xcsv_ifield_add("CONSTANT", "0", "%3s"); /* icon */ + xcsv_ifield_add("CONSTANT", "1", "%2s"); /* 1 */ + xcsv_ifield_add("CONSTANT", "3", "%2s"); /* display format opts */ + xcsv_ifield_add("CONSTANT", "0", "%10s"); /* foreground color */ + xcsv_ifield_add("CONSTANT", "65535", "%10s"); /* background color */ + xcsv_ifield_add("DESCRIPTION", "", "%-40.40s"); + xcsv_ifield_add("CONSTANT", "0", "%2s"); /* pointer direction */ + xcsv_ifield_add("CONSTANT", "0", "%2s"); /* garmin display flags */ + xcsv_ifield_add("CONSTANT", "0", "%5s"); /* proximity distance */ + xcsv_ifield_add("ALT_FEET", "", "%7.0f"); + xcsv_ifield_add("CONSTANT", "6", "%2s"); /* waypt name text size */ + xcsv_ifield_add("CONSTANT", "0", "%2s"); /* bold checkbox */ + xcsv_ifield_add("CONSTANT", "17", "%2s"); /* symbol size */ + + /* outfields are infields */ + if (xcsv_file.ofield) + free(xcsv_file.ofield); + xcsv_file.ofield = &xcsv_file.ifield; + xcsv_file.ofield_ct = xcsv_file.ifield_ct; + + /* set up mkshort */ + if (global_opts.synthesize_shortnames) { + setshort_length(32); + setshort_whitespace_ok(0); + setshort_badchars(xcsv_file.badchars); } } -static void -rd_deinit(void) -{ - fclose(file_in); -} - -static void -wr_init(const char *fname, const char *args) +static void +ozi_rd_init(const char *fname, const char *args) { - file_out = fopen(fname, "w"); - if (file_out == NULL) { - fatal(MYNAME ": Cannot open %s for writing\n", fname); + ozi_set_style(); + + xcsv_file.xcsvfp = fopen(fname, "r"); + + if (xcsv_file.xcsvfp == NULL) { + fatal(MYNAME ": Cannot open %s for reading\n", fname); } } -static void -wr_deinit(void) -{ - fclose(file_out); -} - -static void -data_read(void) -{ - char buff[1024]; - char *s; - waypoint *wpt_tmp; - int i; - int linecount = 0; - double alt; - - do { - linecount++; - memset(buff, '\0', sizeof(buff)); - fgets(buff, sizeof(buff), file_in); - - if ((strlen(buff)) && (strstr(buff, ",") != NULL)) { - - wpt_tmp = xcalloc(sizeof(*wpt_tmp), 1); - - /* data delimited by commas, possibly enclosed in quotes. */ - s = buff; - s = csv_lineparse(s, ",", "", linecount); - - i = 0; - while (s) { - switch (i) { - case 0: - /* sequence # */ - break; - case 1: - /* waypoint name */ - wpt_tmp->shortname = csv_stringtrim(s, ""); - break; - case 2: - /* degrees latitude */ - wpt_tmp->position.latitude.degrees = atof(s); - break; - case 3: - /* degrees longitude */ - wpt_tmp->position.longitude.degrees = atof(s); - break; - case 4: - /* DAYS since 1900 00:00:00 in days.days (5.5) */ - wpt_tmp->creation_time = (atof(s) - 25569.0) * 86400.0; - break; - case 5: - /* icons 0-xx */ - break; - case 6: - /* unknown - always 1 */ - break; - case 7: - /* display format options 0-8 */ - break; - case 8: - /* foreground color (0=black) */ - break; - case 9: - /* background color (65535=yellow)*/ - break; - case 10: - /* Description */ - wpt_tmp->description = csv_stringtrim(s, ""); - - break; - case 11: - /* pointer direction 0,1,2,3 bottom,top,left,right */ - break; - case 12: - /* garmin gps display flags (0-name w/sym, 1-sym only, 2-comment w/symbol */ - break; - case 13: - /* proximity distance - meters */ - break; - case 14: - /* altitude in feet */ - alt = atof(s); - if (alt == -777) { - wpt_tmp->position.altitude.altitude_meters = unknown_alt; - } else { - wpt_tmp->position.altitude.altitude_meters = alt * .3048; - } - break; - case 15: - /* waypoint text name size */ - break; - case 16: - /* bold checkbox (1=bold, default 0) */ - break; - case 17: - /* symbol size - 17 default */ - break; - /* - * Fields 18-23 were added around version 3.90.4g of - * Ozi, but aren't documented. We silently ignore - * these or any additional fields we don't need. - */ - default: - break; - } - i++; - - s = csv_lineparse(NULL, ",", "", linecount); - } - - waypt_add(wpt_tmp); - - } else { - /* empty line */ - } - - } while (!feof(file_in)); -} - -static void -ozi_waypt_pr(const waypoint * wpt) +static void +ozi_wr_init(const char *fname, const char *args) { - static int index = 0; - double alt_feet; - double ozi_time; - char * description; - char * shortname; + ozi_set_style(); - ozi_time = (wpt->creation_time / 86400.0) + 25569.0; - if (wpt->position.altitude.altitude_meters == unknown_alt) { - alt_feet = -777; - } else { - alt_feet = (wpt->position.altitude.altitude_meters * 3.2808); - } + xcsv_file.xcsvfp = fopen(fname, "w"); - if ((! wpt->shortname) || (global_opts.synthesize_shortnames)) { - if (wpt->description) { - if (global_opts.synthesize_shortnames) - shortname = mkshort(wpt->description); - else - shortname = csv_stringclean(wpt->description, ","); - } else { - /* no description available */ - shortname = xstrdup(""); - } - } else{ - shortname = csv_stringclean(wpt->shortname, ","); - } - - if (! wpt->description) { - if (shortname) { - description = csv_stringclean(shortname, ","); - } else { - description = xstrdup(""); - } - } else{ - description = csv_stringclean(wpt->description, ","); + if (xcsv_file.xcsvfp == NULL) { + fatal(MYNAME ": Cannot open %s for reading\n", fname); } - - index++; - - fprintf(file_out, "%4d,%-14.14s,%11.6f,%11.6f,%011.5f,%3d,%2d,%2d,%10d,%10d,%-40.40s,%2d,%2d,%5d,%7.0f,%2d,%2d,%2d\n", - index, shortname, wpt->position.latitude.degrees, - wpt->position.longitude.degrees, ozi_time, 0, 1, 3, 0, 65535, - description, 0, 0, 0, alt_feet, 6, 0, 17); - - free(description); - free(shortname); - } -static void -data_write(void) +static void +ozi_deinit(void) { - fprintf(file_out, "OziExplorer Waypoint File Version 1.1\n"); - fprintf(file_out, "WGS 84\n"); - fprintf(file_out, "Reserved 2\n"); - fprintf(file_out, "Reserved 3\n"); - - if (global_opts.synthesize_shortnames) { - setshort_length(32); - setshort_whitespace_ok(0); - setshort_badchars("\","); - } - - waypt_disp_all(ozi_waypt_pr); + if (xcsv_file.xcsvfp) + fclose(xcsv_file.xcsvfp); + + xcsv_destroy_style(); } ff_vecs_t ozi_vecs = { - rd_init, - wr_init, - rd_deinit, - wr_deinit, - data_read, - data_write, + ozi_rd_init, + ozi_wr_init, + ozi_deinit, + ozi_deinit, + xcsv_data_read, + xcsv_data_write, }; - diff --git a/gpsbabel/reference/xmapwpt.wpt b/gpsbabel/reference/xmapwpt.wpt new file mode 100644 index 000000000..48885aad5 --- /dev/null +++ b/gpsbabel/reference/xmapwpt.wpt @@ -0,0 +1,9 @@ +1296126539:1481466224:1845728360:1416544806:3137157:GCEBB::Mountain Bike Heaven by susy1313 +1296126539:1481466224:1844733052:1420362881:3137157:GC1A37::The Troll by a182pilot & Family +1296126539:1481466224:1845525076:1420861444:3137157:GC1C2B::Dive Bomber by JoGPS & family +1296126539:1481466224:1845170937:1420622369:3137157:GC25A9::FOSTER by JoGPS & Family +1296126539:1481466224:1844552696:1419840970:3137157:GC2723::Logan Lighthouse by JoGps & Family +1296126539:1481466224:1844956189:1419432025:3137157:GC2B71::Ganier Cache by Susy1313 +1296126539:1481466224:1844757518:1419270824:3137157:GC309F::Shy's Hill by FireFighterEng33 +1296126539:1481466224:1845011414:1418580721:3137157:GC317A::GittyUp by JoGPS / Warner Parks +1296126539:1481466224:1844799182:1418788060:3137157:GC317D::Inlighting by JoGPS / Warner Parks diff --git a/gpsbabel/style/README.style b/gpsbabel/style/README.style new file mode 100644 index 000000000..bb56f4c36 --- /dev/null +++ b/gpsbabel/style/README.style @@ -0,0 +1,254 @@ +gpsbabel XCSV Style File Layout: + +The format of an XCSV style file is quite simple and designed to be easily +implemented by non-programmers to handle "one-off" babel-ization of various +XCSV (whatever separated values) text files. The format and usage of the +various style directives are described below. + +The first and foremost important step is understanding how the config +file is laid out itself. The format is: + +DIRECTIVEVALUE + +Where is a space, tab, spaces, tabs, etc... There should +be *nothing* before the directive. (i.e. not " DIRECTIVE VALUE") + +INTERNAL CONSTANTS: +A few internal constants are defined in the XCSV parser to make the style +file simpler. They may or may be used and are optional in most cases. +Note that only certain style file directives map these constants. + +STYLE CONSTANT MAPS TO CHAR(s) +--------------------------------------- +COMMA , +COMMASPACE , +SINGLEQUOTE ' +DOUBLEQUOTE " +COLON : +SEMICOLON ; +NEWLINE \n +CR \r +CRNEWLINE \r\n +TAB \t +SPACE +HASH # + +COMMENTS: +Anything after a hash (#) on a line is not parsed. For example: +#THIS ENTIRE LINE IS A COMMENT. +#FIELD LAT_DECIMAL, "", "%lf" THIS ENTIRE LINE IS A COMMENT +FIELD LAT_DECIMAL, "", "%lf" # ONLY THIS SENTENCE IS A COMMENT. + + +DEFINING THE LAYOUT OF THE FILE: +-------------------------------- +The first few directives define the layout the physical file itself: + + o FIELD_DELIMITER: + The field delimiter defines the character(s) that separate the fields in + the rows of data inside the XCSV file. Common field delimiters are commas + and tabs. (referred to as "comma separated values" and "tab separated + values") + + examples: FIELD_DELIMITER COMMA + FIELD_DELIMITER ~ + + The directive FIELD_DELIMITER is parsed for STYLE CONSTANTS as defined in + the table above. + + o RECORD DELIMITER: + The record delimiter defines that character(s) that separate ROWS of + data (FIELDS) in the XCSV file. The most common record delimiters + are NEWLINE and CR (carriage return). + + example: RECORD_DELIMITER NEWLINE + RECORD_DELIMITER | + + The directive RECORD_DELIMITER is parsed for STYLE CONSTANTS as defined + in the table above. + + o BADCHARS: + Bad characters are things that should *never* be written into the XCSV + file on output. Common bad characters are usually the FIELD_DELIMITER + itself. + + example: BADCHARS COMMA + BADCHARS ~| + + The directive BADCHARS is parsed for STYLE CONSTANTS as defined in the + table above. + + o PROLOGUE + A prologue is basically constant data that is written to the output + file BEFORE any waypoints are processed. PROLOGUE can be defined + multiple times in the style file, once for each "line" before the data + begins. This is commonly used in XCSV files as a "header" row. + + example: PROLOGUE OziExplorer Waypoint File Version 1.1 + PROLOGUE WGS 84 + * or * + PROLOGUE Symbol,Name,Latitude,Longitude + + o EPILOGUE + An Epilogue is the same as a prologue, except this data is written at + the END of the file. See the examples for PROLOGUE above. + + +DEFINING FIELDS WITHIN THE FILE: +------------------------------- + +A field defines data. There are two different classifications of FIELDS, +IFIELD (file input) and OFIELD (file output). In the absence of any OFIELDS, +IFIELDS are use as both input and output. The existence of OFIELDS is +primarily to allow more flexible mapping of gpsbabel data to output data +(say, for instance, to map the internal gpsbabel "description" variable to +two or more fields on output). For all practical purposes, IFIELDS and +OFIELDS are defined the same way in the style file. + +There are several different types of fields that may be defined. Each field +consists of three pieces of information: the FIELD TYPE, a DEFAULT VALUE, and +a PRINTF CONVERSION (for output). In many cases, not all pieces are used, +but all 3 pieces are required. + +FIELDS should be defined in the style file in the logical order that they +appear in the data, from left to right. This is the order in which they are +parsed from input and written to output. + +The fields used by the XCSV parser are as follows: + + o IGNORE + IGNORE fields are, guess what, ignored on input. Internally, IGNORE + fields are treated as CHARACTER data, and as such, require a printf + conversion for a character array. + + example: IFIELD IGNORE,"","%14.14s" (writes a 14 character blank field) + IFIELD IGNORE,"","%s" (writes a blank field on output) + + o CONSTANT + CONSTANT fields are, of course, constant. They are ignored on input, + however they write CONSTANT data on output. As such, they require a + DEFAULT VALUE and a printf conversion for a character array. + + example: IFIELD CONSTANT,"FFFFFF","%s" (writes "FFFFFF" in the field) + IFIELD CONSTANT,"01/01/70","%s" (a constant date field) + + o INDEX + An INDEX field is used ONLY on output. The INDEX constant defines a field + that, at output, contains the sequence number of the waypoint being + written, starting at 0. An index is managed internally as an INTEGER + and requires an INTEGER printf conversion. An INDEX has one special + property. The DEFAULT VALUE of the index is added to the index + on each iteration (to allow indexes starting at 1, 100, etc..). + + example: IFIELD INDEX,"0","%04d" (Starts counting at zero) + IFIELD INDEX,"","%04d" (Starts counting at zero) + IFIELD INDEX,"1","%04d" (Starts counting at one) + + o SHORTNAME + A SHORTNAME is generally the waypoint name of the data being processed. + SHORTNAME maps directly to the gpsbabel variable ->shortname. A SHORTNAME + is CHARACTER data and requires a character array printf conversion. + + example: IFIELD SHORTNAME,"","%s" (write shortname in the output file) + + o DESCRIPTION + A DESCRIPTION is generally a long description of the waypoint. A + DESCRIPTION maps to the gpsbabel variable ->description and is otherwise + handled exactly like a SHORTNAME. + + example: IFIELD DESCRIPTION,"","%s" (write description in the output file) + + o NOTES + NOTES are generally everything else about a waypoints. NOTES map to the + gpsbabel variable ->notes and is otherwise handled exactly like a + SHORTNAME. + + o URL + URL is a URL for the waypoint. URL maps to the gpsbabel variable + ->url and is otherwise handled exactly like a SHORTNAME. + + example: IFIELD URL,"","%s" (writes the URL in the output file) + + o URL_LINK_TEXT + URL_LINK_TEXT is a textual description of where a URL points. + URL_LINK_TEXT maps to the gpsbabel variable ->url_link_text and + is otherwise handled exactly like a SHORTNAME. + + example: IFIELD URL_LINK_TEXT,"","%s" (writes link text in the output file) + + o ICON_DESCR + ICON_DESCR is a textual description of an icon type for a waypoint. + ICON_DESCR maps to the gpsbabel variable ->icon_desc and is otherwise + handled exactly like a SHORTNAME. + + example: IFIELD ICON_DESCR,"","%s" (writes link text in the output file) + + o LAT_DECIMAL + LAT_DECIMAL defines LATITUDE in DECIMAL format. Note that this is a PURE + signed decimal format (i.e. -91.0000). This data is handled internally as + a DOUBLE PRECISION FLOAT and requires a FLOATING POINT printf conversion. + + example: IFIELD LAT_DECIMAL,"","%lf" + + o LON_DECIMAL + See LAT_DECIMAL, except LON_DECIMAL defines LONGITUDE. + + o LAT_INT32DEG + LAT_INT32DEG defines LATITUDE in what I call INT32DEGREES. This value is + a signed LONG INTEGER and requires a LONG INTEGER printf conversion. + + example: IFIELD LAT_INT32DEG,"","%ld" + + o LON_INT32DEG + See LON_INT32DEG except LON_INT32DEG defines LONGITUDE. + + o LAT_DECIMALDIR / LAT_DIRDECIMAL + LAT_DECIMALDIR and LAT_DIRDECIMAL defines LATITUDE in DECIMAL format + with the added bonus of a 'N/S' or 'E/W' direction character. This data + is handled internally as a DOUBLE PRECISION FLOAT and a single + CHARACTER and requires a FLOATING POINT as well as a CHARACTER printf + conversion. The only difference between the two is whether the directional + character appears before (LAT_DIRDECIMAL) or after (LAT_DECIMALDIR) the + decimal number. + + example: IFIELD LAT_DECIMALDIR,"","%lf %c" + example: IFIELD LAT_DIRDECIMAL,"","%c %08.5lf" + + o LON_DECIMALDIR / LON_DIRDECIMAL + Same as LAT_DECIMALDIR / LAT_DIRDECIMAL except LON_ defines LONGITUDE. + + + o ALT_FEET + ALT_FEET is the position's ALTITUDE in FEET. This value is treated as + a SIGNED DOUBLE PRECISION FLOAT and requires a FLOATING POINT printf + conversion. + + example: IFIELD ALT_FEET,"","%.0lf" + + o ALT_METERS + ALT_METERS is identical to ALT_FEET with the exception that the altitude + is in METERS. + + o EXCEL_TIME + EXCEL_TIME is the waypoint's creation time, if any. This is actually + the decimal days since 1/1/1900 and is handled internally as a DOUBLE + PRECISION FLOAT and requires a FLOATING POINT printf conversion. + + example: IFIELD EXCEL_TIME,"","%11.5lf" + + o TIMET_TIME + TIMET_TIME is the waypoint's creation time, if any. This is actually + the integer seconds since 1/1/1970 (let's not start the holy war) and + is handled internally as a LONG INTEGER and requires a LONG INTEGER + printf conversion. + + example: IFIELD TIMET_TIME,"","%ld" + + +EXAMPLES: +-------- +For examples on using the XCSV module, please see the *.style files in +the style/ subdirectory of gpsbabel. For examples of using the XCSV +module instead of carving out trivial C code, see the source code +examples ozi.c, mxf.c, and xmapwpt.c in the gpsbabel directory. + diff --git a/gpsbabel/style/csv.style b/gpsbabel/style/csv.style new file mode 100644 index 000000000..8866a73f4 --- /dev/null +++ b/gpsbabel/style/csv.style @@ -0,0 +1,22 @@ +# gpsbabel XCSV style file +# +# Format: Delorme SA 9.0 CSV +# Author: Alex Mottram +# Date: 12/09/2002 +# +# +# As defined in csv.c +# +# +# FILE LAYOUT DEFINITIIONS: +# +FIELD_DELIMITER COMMASPACE +RECORD_DELIMITER NEWLINE +BADCHARS COMMA + +# +# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE: +# +IFIELD LAT_DECIMAL, "", "%08.5lf" +IFIELD LON_DECIMAL, "", "%08.5lf" +IFIELD DESCRIPTION, "", "%s" diff --git a/gpsbabel/style/custom.style b/gpsbabel/style/custom.style new file mode 100644 index 000000000..af22154c3 --- /dev/null +++ b/gpsbabel/style/custom.style @@ -0,0 +1,48 @@ +# gpsbabel XCSV style file +# +# Format: Custom "Everything" Style +# Author: Alex Mottram +# Date: 11/24/2002 +# +# +# FILE LAYOUT DEFINITIIONS: +# +FIELD_DELIMITER COMMA +RECORD_DELIMITER NEWLINE +BADCHARS COMMA + +# +# HEADER STUFF: +# +PROLOGUE Prologue Line 1 +PROLOGUE Prologue Line 2 + +# +# INDIVIDUAL DATA FIELDS: +# +IFIELD CONSTANT, "", "CONSTANT" +IFIELD INDEX, "", "%d" +IFIELD LAT_DECIMAL, "", "%lf" +IFIELD LAT_DIR, "", "%c" +IFIELD LON_DECIMAL, "", "%lf" +IFIELD LON_DIR, "", "%c" +IFIELD ICON_DESCR, "", "%s" +IFIELD SHORTNAME, "", "%s" +IFIELD DESCRIPTION, "", "%s" +IFIELD NOTES, "", "%s" +IFIELD URL, "", "%s" +IFIELD URL_LINK_TEXT, "", "%s" +IFIELD ALT_METERS, "", "%lfM" +IFIELD ALT_FEET, "", "%lfF" +IFIELD LAT_DECIMALDIR, "", "%lf/%c" +IFIELD LON_DECIMALDIR, "", "%lf/%c" +IFIELD LAT_DIRDECIMAL, "", "%c/%lf" +IFIELD LON_DIRDECIMAL, "", "%c/%lf" +IFIELD LAT_INT32DEG, "", "%ld" +IFIELD LON_INT32DEG, "", "%ld" +IFIELD TIMET_TIME, "", "%ld" +IFIELD EXCEL_TIME, "", "%lf" + +# EPILOGUE: +EPILOGUE Epilogue Line 1 +EPILOGUE Epilogue Line 2 diff --git a/gpsbabel/style/dna.style b/gpsbabel/style/dna.style new file mode 100644 index 000000000..d94dc392a --- /dev/null +++ b/gpsbabel/style/dna.style @@ -0,0 +1,24 @@ +# gpsbabel XCSV style file +# +# Format: DNA Marker Format +# Author: Alex Mottram +# Date: 12/09/2002 +# +# +# As defined in dna.c +# +# +# FILE LAYOUT DEFINITIIONS: +# +FIELD_DELIMITER COMMA +RECORD_DELIMITER NEWLINE +BADCHARS COMMA + +# +# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE: +# +IFIELD INDEX, "", "%d" +IFIELD LAT_DECIMAL, "", "%08.5lf" +IFIELD LON_DECIMAL, "", "%08.5lf" +IFIELD DESCRIPTION, "", "%s" + diff --git a/gpsbabel/style/gpsdrive.style b/gpsbabel/style/gpsdrive.style new file mode 100644 index 000000000..e743bcd2a --- /dev/null +++ b/gpsbabel/style/gpsdrive.style @@ -0,0 +1,20 @@ +# gpsbabel XCSV style file +# +# Format: GPSDrive +# Author: Alex Mottram +# Date: 12/11/2002 +# +# +# +# FILE LAYOUT DEFINITIIONS: +# +FIELD_DELIMITER SPACE +RECORD_DELIMITER NEWLINE +BADCHARS ," + +# +# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE: + +IFIELD ANYNAME, "", "%s" +IFIELD LAT_DECIMAL, "", "%08.5lf" +IFIELD LON_DECIMAL, "", "%08.5lf" diff --git a/gpsbabel/style/gpsman.style b/gpsbabel/style/gpsman.style new file mode 100644 index 000000000..e6b74c825 --- /dev/null +++ b/gpsbabel/style/gpsman.style @@ -0,0 +1,29 @@ +# gpsbabel XCSV style file +# +# Format: GPSMAN Format +# Author: Alex Mottram +# Date: 12/09/2002 +# +# +# As defined in gpsman.c +# +# +# FILE LAYOUT DEFINITIIONS: +# +FIELD_DELIMITER TAB +RECORD_DELIMITER NEWLINE +BADCHARS TAB + +PROLOGUE !Format: DDD 1 WGS 84 +PROLOGUE !W: + +# +# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE: +# +IFIELD SHORTNAME, "", "%-8.8s" +IFIELD DESCRIPTION, "", "%s" +IFIELD LAT_DIRDECIMAL, "", "%c%lf" +IFIELD LON_DIRDECIMAL, "", "%c%lf" +IFIELD IGNORE, "", "%s" + +# gpsman.c likes mkshort len = 8, whitespace = 0. diff --git a/gpsbabel/style/mxf.style b/gpsbabel/style/mxf.style new file mode 100644 index 000000000..4d00c9a0d --- /dev/null +++ b/gpsbabel/style/mxf.style @@ -0,0 +1,34 @@ +# gpsbabel XCSV style file +# +# Format: Ozi Explorer +# Author: Alex Mottram +# Date: 12/09/2002 +# +# +# As used in mxf.c +# +# +# FILE LAYOUT DEFINITIIONS: +# +FIELD_DELIMITER COMMASPACE +RECORD_DELIMITER NEWLINE +BADCHARS COMMA + +# +# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE: +# +IFIELD LAT_DECIMAL, "", "%08.5f" +IFIELD LON_DECIMAL, "", "%08.5f" +IFIELD DESCRIPTION, "", "%s" +IFIELD SHORTNAME, "", "%s" +IFIELD IGNORE, "", "%s" +IFIELD CONSTANT, "ff0000", "%s" # COLOR +IFIELD CONSTANT, "47", "%s" # ICON + +OFIELD LAT_DECIMAL, "", "%08.5f" +OFIELD LON_DECIMAL, "", "%08.5f" +OFIELD DESCRIPTION, "", ""%s"" +OFIELD SHORTNAME, "", "%s" +OFIELD DESCRIPTION, "", "%s" +OFIELD CONSTANT, "ff0000", "%s" # COLOR +OFIELD CONSTANT, "47", "%s" # ICON diff --git a/gpsbabel/style/nima.style b/gpsbabel/style/nima.style new file mode 100644 index 000000000..a9b684528 --- /dev/null +++ b/gpsbabel/style/nima.style @@ -0,0 +1,42 @@ +# gpsbabel XCSV style file +# +# Format: NIMA/GNIS Geographic Names File +# Author: Alex Mottram +# Date: 11/24/2002 +# +# +# FILE LAYOUT DEFINITIIONS: +# +FIELD_DELIMITER TAB +RECORD_DELIMITER NEWLINE +BADCHARS TAB +PROLOGUE RC UFI UNI DD_LAT DD_LONG DMS_LAT DMS_LONG UTM JOG FC DSG PC CC1 ADM1 ADM2 DIM CC2 NT LC SHORT_FORM GENERIC SORT_NAME FULL_NAME FULL_NAME_ND MODIFY_DATE + +# +# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE: +# +IFIELD IGNORE, "", "%s" # RC +IFIELD IGNORE, "", "%s" # UFI +IFIELD IGNORE, "", "%s" # UNI +IFIELD LAT_DECIMAL, "", "%lf" # DD_LAT +IFIELD LON_DECIMAL, "", "%lf" # DD_LON +IFIELD IGNORE, "", "%s" # DMS_LAT +IFIELD IGNORE, "", "%s" # DMS_LON +IFIELD IGNORE, "", "%s" # UTM +IFIELD IGNORE, "", "%s" # JOG +IFIELD IGNORE, "", "%s" # FC +IFIELD IGNORE, "", "%s" # DSG +IFIELD IGNORE, "", "%s" # PC +IFIELD IGNORE, "", "%s" # CC1 +IFIELD IGNORE, "", "%s" # ADM1 +IFIELD IGNORE, "", "%s" # ADM2 +IFIELD IGNORE, "", "%s" # DIM +IFIELD IGNORE, "", "%s" # CC2 +IFIELD IGNORE, "", "%s" # NT +IFIELD IGNORE, "", "%s" # LC +IFIELD IGNORE, "", "%s" # SHORT_FORM +IFIELD IGNORE, "", "%s" # GENERIC +IFIELD SHORTNAME, "", "%s" # SORT_NAME +IFIELD IGNORE, "", "%s" # FULL_NAME (unicoded!) +IFIELD DESCRIPTION, "", "%s" # FULL_NAME_ND +IFIELD IGNORE, "", "%s" # MODIFY_DATE diff --git a/gpsbabel/style/ozi.style b/gpsbabel/style/ozi.style new file mode 100644 index 000000000..0acea285d --- /dev/null +++ b/gpsbabel/style/ozi.style @@ -0,0 +1,42 @@ +# gpsbabel XCSV style file +# +# Format: Ozi Explorer +# Author: Alex Mottram +# Date: 12/09/2002 +# +# +# As used in ozi.c +# +# +# FILE LAYOUT DEFINITIIONS: +# +FIELD_DELIMITER COMMA +RECORD_DELIMITER NEWLINE +BADCHARS COMMA + +PROLOGUE OziExplorer Waypoint File Version 1.1 +PROLOGUE WGS 84 +PROLOGUE Reserved 2 +PROLOGUE Reserved 3 + +# +# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE: +# +IFIELD INDEX, "1", "%4d" +IFIELD SHORTNAME, "", "%-14.14s" +IFIELD LAT_DECIMAL, "", "%11.6lf" +IFIELD LON_DECIMAL, "", "%11.6lf" +IFIELD EXCEL_TIME, "", "%011.5lf" +IFIELD CONSTANT, "0", "%3s" # icon +IFIELD CONSTANT, "1", "%2s" # 1 +IFIELD CONSTANT, "3", "%2s" # display format opts +IFIELD CONSTANT, "0", "%10s" # foreground color +IFIELD CONSTANT, "65535", "%10s" # background color +IFIELD DESCRIPTION, "", "%-40.40s" +IFIELD CONSTANT, "0", "%2s" # pointer direction +IFIELD CONSTANT, "0", "%2s" # garmin display flags +IFIELD CONSTANT, "0", "%5s" # proximity distance +IFIELD ALT_FEET, "", "%7.0lf" +IFIELD CONSTANT, "6", "%2s" # waypt name text size +IFIELD CONSTANT, "0", "%2s" # bold checkbox +IFIELD CONSTANT, "17", "%2s" # symbol size diff --git a/gpsbabel/style/s_and_t.style b/gpsbabel/style/s_and_t.style new file mode 100644 index 000000000..11a418d45 --- /dev/null +++ b/gpsbabel/style/s_and_t.style @@ -0,0 +1,33 @@ +# gpsbabel XCSV style file +# +# Format: MS S&T 2002/2003 +# Author: Alex Mottram +# Date: 12/09/2002 +# +# +# As requested by Noel Shrum on the gpsbabel-code mailing list. +# Name,Latitude,Longitude,Name 2,URL,Type +# GCCBF,44.479133,-85.56515,High Rollaway by rjlint,http://www.geocaching.com/seek/cache_details.aspx?ID=3263,Traditional Cache +# GC110D,44.6522,-85.492483,Brown Bridge Pond Peek-a-Boo Cache by Big Bird,http://www.geocaching.com/seek/cache_details.aspx?ID=4365,Traditional Cache +# GC171C,44.70605,-85.62265,The Michigan Frog by RealDcoy & LRB,http://www.geocaching.com/seek/cache_details.aspx?ID=5916,Traditional Cache +# +# +# FILE LAYOUT DEFINITIIONS: +# +FIELD_DELIMITER COMMA +RECORD_DELIMITER NEWLINE +BADCHARS COMMA + +PROLOGUE Name,Latitude,Longitude,Name 2,URL,Type + +# +# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE: +# NOTE: MS S&T ONLY IMPORTS DATA, IT DOESN'T EXPORT THIS ANYWHERE SO WE CAN +# HAVE OUR WAY WITH THE FORMATTING. +# +IFIELD SHORTNAME, "", "%s" # Name +IFIELD LAT_DECIMAL, "", "%lf" # Latitude +IFIELD LON_DECIMAL, "", "%lf" # Longitude +IFIELD DESCRIPTION, "", "%s" # Name 2 (Big Description) +IFIELD URL, "", "%s" # URL +IFIELD IGNORE, "", "" # Holder for Geocache Type diff --git a/gpsbabel/style/tiger.style b/gpsbabel/style/tiger.style new file mode 100644 index 000000000..8254ea974 --- /dev/null +++ b/gpsbabel/style/tiger.style @@ -0,0 +1,23 @@ +# gpsbabel XCSV style file +# +# Format: Tiger Data Format +# Author: Alex Mottram +# Date: 12/09/2002 +# +# +# As defined in tiger.c +# +# +# FILE LAYOUT DEFINITIIONS: +# +FIELD_DELIMITER COLON +RECORD_DELIMITER NEWLINE +BADCHARS COLON + +# +# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE: +# +IFIELD LON_DECIMAL, "", "%lf" +IFIELD LAT_DECIMAL, "", "%lf" +IFIELD CONSTANT, "redpin", "%s" +IFIELD DESCRIPTION, "", "%s" diff --git a/gpsbabel/style/xmap.style b/gpsbabel/style/xmap.style new file mode 100644 index 000000000..7570667f5 --- /dev/null +++ b/gpsbabel/style/xmap.style @@ -0,0 +1,24 @@ +# gpsbabel XCSV style file +# +# Format: Delorme Xmap Conduit +# Author: Alex Mottram +# Date: 12/09/2002 +# +# +# As defined in csv.c/xmap +# +# +# FILE LAYOUT DEFINITIIONS: +# +FIELD_DELIMITER COMMASPACE +RECORD_DELIMITER NEWLINE +BADCHARS COMMA + +PROLOGUE BEGIN SYMBOL +EPILOGUE END +# +# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE: +# +IFIELD LAT_DECIMAL, "", "%08.5lf" +IFIELD LON_DECIMAL, "", "%08.5lf" +IFIELD DESCRIPTION, "", "%s" diff --git a/gpsbabel/testo b/gpsbabel/testo index 57145ed89..577ab6080 100755 --- a/gpsbabel/testo +++ b/gpsbabel/testo @@ -166,3 +166,24 @@ diff ${TMPDIR}/gpsdrive.txt reference ${PNAME} -i gpsdrive -f reference/gpsdrive.txt -o gpsdrive -F ${TMPDIR}/gpsdrive2.txt diff ${TMPDIR}/gpsdrive2.txt reference/gpsdrive.txt +# XMapHH Street Atlas USA file format +rm -f ${TMPDIR}/xmapwpt.wpt ${TMPDIR}/xmapwpt.xmapwpt +${PNAME} -i xmapwpt -f reference/xmapwpt.wpt -o xmapwpt -F ${TMPDIR}/xmapwpt.xmapwpt +${PNAME} -i xmapwpt -f ${TMPDIR}/xmapwpt.xmapwpt -o xmapwpt -F ${TMPDIR}/xmapwpt.wpt +diff ${TMPDIR}/xmapwpt.wpt reference + +# XCSV +# Test that we can parse a style file, and read and write data in the +# same xcsv format (a complete test is virtually impossible). +echo "RECORD_DELIMITER NEWLINE" > ${TMPDIR}/testo.style +echo "FIELD_DELIMITER COMMA" >> ${TMPDIR}/testo.style +echo "BADCHARS COMMA" >> ${TMPDIR}/testo.style +echo "PROLOGUE Header" >> ${TMPDIR}/testo.style +echo "EPILOGUE Footer" >> ${TMPDIR}/testo.style +echo "IFIELD SHORTNAME,,%s" >> ${TMPDIR}/testo.style +echo "IFIELD LAT_DIRDECIMAL,,%c%lf" >> ${TMPDIR}/testo.style +echo "IFIELD LON_DECIMALDIR,,%lf%c" >> ${TMPDIR}/testo.style +rm -f ${TMPDIR}/xcsv.geo ${TMPDIR}/xcsv.xcsv +${PNAME} -i geo -f geocaching.loc -o xcsv,style=${TMPDIR}/testo.style -F ${TMPDIR}/xcsv.geo +${PNAME} -i xcsv,style=${TMPDIR}/testo.style -f ${TMPDIR}/xcsv.geo -o xcsv,style=${TMPDIR}/testo.style -F ${TMPDIR}/xcsv.xcsv +diff -u ${TMPDIR}/xcsv.geo ${TMPDIR}/xcsv.xcsv diff --git a/gpsbabel/vecs.c b/gpsbabel/vecs.c index 193f6f387..6afd9856b 100644 --- a/gpsbabel/vecs.c +++ b/gpsbabel/vecs.c @@ -46,10 +46,12 @@ extern ff_vecs_t garmin_vecs; extern ff_vecs_t mxf_vecs; extern ff_vecs_t holux_vecs; extern ff_vecs_t ozi_vecs; +extern ff_vecs_t xcsv_vecs; extern ff_vecs_t tpg_vecs; extern ff_vecs_t dna_vecs; extern ff_vecs_t magnav_vec; extern ff_vecs_t xmap_vecs; +extern ff_vecs_t xmapwpt_vecs; extern ff_vecs_t tmpro_vecs; extern ff_vecs_t gpsdrive_vecs; @@ -122,6 +124,12 @@ vecs_t vec_list[] = { "Delorme Topo USA4/XMap Conduit", NULL }, + { + &xmapwpt_vecs, + "xmapwpt", + "Delorme XMap HH Native .WPT", + ".wpt" + }, { &dna_vecs, "dna", @@ -176,6 +184,12 @@ vecs_t vec_list[] = { "OziExplorer Waypoint", "ozi" }, + { + &xcsv_vecs, + "xcsv", + "? Character Separated Values", + NULL + }, { &tpg_vecs, "tpg", diff --git a/gpsbabel/xcsv.c b/gpsbabel/xcsv.c new file mode 100644 index 000000000..b03bb14dd --- /dev/null +++ b/gpsbabel/xcsv.c @@ -0,0 +1,383 @@ +/* + XCSV - X Character Separated Values (.???) + + A hopefully not too feeble attempt at parsing whatever separated values + files into the waypoint structure and back out again. This is a config- + file wrapper around csv_util.c. + + Copyright (C) 2002 Alex Mottram (geo_alexm at cox-internet.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA + + */ + +#include +#include "defs.h" +#include "csv_util.h" + +#define MYNAME "XCSV" +#define ISSTOKEN(a,b) (strncmp(a,b, strlen(b)) == 0) + +/* a table of config file constants mapped to chars */ +static +char_map_t xcsv_char_table[] = { + { "COMMA", "," }, + { "COMMASPACE", ", " }, + { "SINGLEQUOTE", "'" }, + { "DOUBLEQUOTE", "\"" }, + { "COLON", ":" }, + { "SEMICOLON", ";" }, + { "NEWLINE", "\n" }, + { "CR", "\n" }, + { "CRNEWLINE", "\r\n" }, + { "TAB", "\t" }, + { "SPACE", " " }, + { "HASH", "#" }, + { NULL, NULL } +}; + +void +xcsv_destroy_style(void) +{ + queue *elem, *tmp; + field_map_t *fmp; + ogue_t *ogp; + + /* + * If this xcsv_file struct came from a file we can free it all. + * If not, we can at least free the queue elements. + */ + + /* destroy the prologue */ + QUEUE_FOR_EACH(&xcsv_file.prologue, elem, tmp) { + if (xcsv_file.is_internal == 0) { + ogp = (ogue_t *)elem; + if (ogp->val) + free(ogp->val); + } + if (elem) + free(elem); + } + + /* destroy the epilogue */ + QUEUE_FOR_EACH(&xcsv_file.epilogue, elem, tmp) { + if (xcsv_file.is_internal == 0) { + ogp = (ogue_t *)elem; + if (ogp->val) + free(ogp->val); + } + if (elem) + free(elem); + } + + /* destroy the ifields */ + QUEUE_FOR_EACH(&xcsv_file.ifield, elem, tmp) { + if (xcsv_file.is_internal == 0) { + fmp = (field_map_t *) elem; + if (fmp->key) + free(fmp->key); + if (fmp->val) + free(fmp->val); + if (fmp->printfc) + free(fmp->printfc); + } + if (elem) + free(elem); + } + + /* destroy the ofields, if they are not re-mapped to ifields. */ + if (xcsv_file.ofield != &xcsv_file.ifield) { + QUEUE_FOR_EACH(xcsv_file.ofield, elem, tmp) { + if (xcsv_file.is_internal == 0) { + fmp = (field_map_t *) elem; + if (fmp->key) + free(fmp->key); + if (fmp->val) + free(fmp->val); + if (fmp->printfc) + free(fmp->printfc); + } + if (elem) + free(elem); + } + + if (xcsv_file.ofield) + free(xcsv_file.ofield); + } + + if (xcsv_file.is_internal == 0) { + /* other alloc'd glory */ + if (xcsv_file.field_delimiter) + free(xcsv_file.field_delimiter); + + if (xcsv_file.record_delimiter) + free(xcsv_file.record_delimiter); + + if (xcsv_file.badchars) + free(xcsv_file.badchars); + } + + /* return everything to zeros */ + memset(&xcsv_file, '\0', sizeof(xcsv_file)); +} + +static const char * +get_char_from_constant_table(char *key) +{ + char_map_t *cm = xcsv_char_table; + + while ((cm->key) && (strcmp(key, cm->key) != 0)) { + cm++; + } + + return (cm->chars); +} + +static void +xcsv_read_style(const char *fname) +{ + char sbuff[8192]; + int i, linecount = 0; + FILE *fp; + char *s, *p, *sp; + const char *cp; + char *key, *val, *pfc; + + xcsv_file_init(); + + fp = fopen(fname, "r"); + + if (!fp) + fatal(MYNAME ": Cannot read style file: %s\n", fname); + + do { + memset(sbuff, '\0', sizeof(sbuff)); + fgets(sbuff, sizeof(sbuff), fp); + rtrim(sbuff); + + /* + * tokens should be parsed longest to shortest, unless something + * requires a previously set value. This way something like + * SHORT and SHORTNAME don't collide. + */ + + /* whack off any comments */ + if ((p = strchr(sbuff, '#')) != NULL) + *p = '\0'; + + if (strlen(sbuff)) { + if (ISSTOKEN(sbuff, "FIELD_DELIMITER")) { + sp = csv_stringtrim(&sbuff[16], "\""); + cp = get_char_from_constant_table(sp); + if (cp) + xcsv_file.field_delimiter = xstrdup(cp); + else + xcsv_file.field_delimiter = sp; + } else + + if (ISSTOKEN(sbuff, "RECORD_DELIMITER")) { + sp = csv_stringtrim(&sbuff[17], "\""); + cp = get_char_from_constant_table(sp); + if (cp) + xcsv_file.record_delimiter = xstrdup(cp); + else + xcsv_file.field_delimiter = sp; + } else + + if (ISSTOKEN(sbuff, "BADCHARS")) { + sp = csv_stringtrim(&sbuff[9], "\""); + cp = get_char_from_constant_table(sp); + if (cp) + xcsv_file.badchars = xstrdup(cp); + else + xcsv_file.badchars = sp; + } else + + if (ISSTOKEN(sbuff, "PROLOGUE")) { + xcsv_prologue_add(xstrdup(&sbuff[9])); + } else + + if (ISSTOKEN(sbuff, "EPILOGUE")) { + xcsv_epilogue_add(xstrdup(&sbuff[9])); + } else + + if (ISSTOKEN(sbuff, "IFIELD")) { + key = val = pfc = NULL; + + s = csv_lineparse(&sbuff[6], ",", "", linecount); + + i = 0; + while (s) { + switch(i) { + case 0: + /* key */ + key = csv_stringtrim(s, "\""); + break; + case 1: + /* default value */ + val = csv_stringtrim(s, "\""); + break; + case 2: + /* printf conversion */ + pfc = csv_stringtrim(s, "\""); + break; + default: + break; + } + i++; + + s = csv_lineparse(NULL, ",", "", linecount); + } + + xcsv_ifield_add(key, val, pfc); + + } else + + /* + * as OFIELDs are implemented as an after-thought, I'll + * leave this as it's own parsing for now. We could + * change the world on ifield vs ofield format later.. + */ + if (ISSTOKEN(sbuff, "OFIELD")) { + key = val = pfc = NULL; + + s = csv_lineparse(&sbuff[6], ",", "", linecount); + + i = 0; + while (s) { + switch(i) { + case 0: + /* key */ + key = csv_stringtrim(s, "\""); + break; + case 1: + /* default value */ + val = csv_stringtrim(s, "\""); + break; + case 2: + /* printf conversion */ + pfc = csv_stringtrim(s, "\""); + break; + default: + break; + } + i++; + s = csv_lineparse(NULL, ",", "", linecount); + } + + xcsv_ofield_add(key, val, pfc); + } + } + } while (!feof(fp)); + + /* if we have no output fields, use input fields as output fields */ + if (xcsv_file.ofield_ct == 0) { + if (xcsv_file.ofield) + free(xcsv_file.ofield); + xcsv_file.ofield = &xcsv_file.ifield; + xcsv_file.ofield_ct = xcsv_file.ifield_ct; + } + + fclose(fp); +} + +static void +xcsv_rd_init(const char *fname, const char *args) +{ + const char *p; + + /* + * if we don't have an internal style defined, we need to + * read it from a user-supplied style file, or die trying. + */ + if (xcsv_file.is_internal == 0) { + p = get_option(args, "style"); + + if (!p) + fatal(MYNAME ": XCSV input style not declared. Use ... -i xcsv,style=path/to/file.style\n"); + + xcsv_read_style(p); + } + + xcsv_file.xcsvfp = fopen(fname, "r"); + + if (xcsv_file.xcsvfp == NULL) + fatal(MYNAME ": Cannot open %s for reading\n", fname); +} + +static void +xcsv_rd_deinit(void) +{ + fclose(xcsv_file.xcsvfp); + + xcsv_destroy_style(); +} + +static void +xcsv_wr_init(const char *fname, const char *args) +{ + const char * p; + + /* if we don't have an internal style defined, we need to + * read it from a user-supplied style file, or die trying. + */ + if (xcsv_file.is_internal == 0) { + p = get_option(args, "style"); + + if (!p) + fatal(MYNAME ": XCSV output style not declared. Use ... -o xcsv,style=path/to/file.style\n"); + + xcsv_read_style(p); + + /* set mkshort options from the command line */ + if (global_opts.synthesize_shortnames) { + p = get_option(args, "snlen"); + if (p) + setshort_length(atoi(p)); + + p = get_option(args, "snwhite"); + if (p) + setshort_whitespace_ok(atoi(p)); + + p = get_option(args, "snupper"); + if (p) + setshort_mustupper(atoi(p)); + + setshort_badchars(xcsv_file.badchars); + } + } + + xcsv_file.xcsvfp = fopen(fname, "w"); + + if (xcsv_file.xcsvfp == NULL) + fatal(MYNAME ": Cannot open %s for writing\n", fname); +} + +static void +xcsv_wr_deinit(void) +{ + fclose(xcsv_file.xcsvfp); + + xcsv_destroy_style(); +} + +ff_vecs_t xcsv_vecs = { + xcsv_rd_init, + xcsv_wr_init, + xcsv_rd_deinit, + xcsv_wr_deinit, + xcsv_data_read, + xcsv_data_write, +}; diff --git a/gpsbabel/xmapwpt.c b/gpsbabel/xmapwpt.c new file mode 100644 index 000000000..c1e834f79 --- /dev/null +++ b/gpsbabel/xmapwpt.c @@ -0,0 +1,115 @@ +/* + Delorme XMap HandHeld .WPT Format + (as created by XMapHH Street Atlas/PPC) + 1296126539:1481466224:1895825408:1392508928:3137157:text:text:text\n + + Contributed to gpsbabel by Alex Mottram (geo_alexm at cox-internet.com) + + Copyright (C) 2002 Robert Lipe, robertlipe@usa.net + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA + + */ + +#include "defs.h" +#include "csv_util.h" + +#define MYNAME "XMAPWPT" + +static void +xmapwpt_set_style() +{ + /* set up the xmapwpt xcsv_file struct */ + xcsv_file_init(); + + /* this is an internal style, don't mess with it */ + xcsv_file.is_internal = 1; + + /* how the file gets split up */ + xcsv_file.field_delimiter = ":"; + xcsv_file.record_delimiter = "\n"; + xcsv_file.badchars = ":"; + + xcsv_ifield_add("CONSTANT", "1296126539", "%s"); + xcsv_ifield_add("CONSTANT", "1481466224", "%s"); + xcsv_ifield_add("LAT_INT32DEG", "", "%d"); + xcsv_ifield_add("LON_INT32DEG", "", "%d"); + xcsv_ifield_add("CONSTANT", "3137157", "%s"); + xcsv_ifield_add("SHORTNAME", "", "%-.31s"); + xcsv_ifield_add("IGNORE", "", "%-.31s"); + + /* + * actual description len accepted is 79. however under win32, we + * run the risk of the compiled app ending a line in \r\n when we + * say \n. This, in turn, overruns a fixed len buffer and causes + * XmapHH to die both occasionally and horribly. + */ + xcsv_ifield_add("DESCRIPTION", "", "%-.78s"); + + /* outfields are infields */ + if (xcsv_file.ofield) + free(xcsv_file.ofield); + xcsv_file.ofield = &xcsv_file.ifield; + xcsv_file.ofield_ct = xcsv_file.ifield_ct; + + /* set up mkshort */ + if (global_opts.synthesize_shortnames) { + setshort_length(32); + setshort_whitespace_ok(0); + setshort_badchars(xcsv_file.badchars); + } +} + +static void +xmapwpt_rd_init(const char *fname, const char *args) +{ + xmapwpt_set_style(); + + xcsv_file.xcsvfp = fopen(fname, "r"); + + if (xcsv_file.xcsvfp == NULL) { + fatal(MYNAME ": Cannot open %s for reading\n", fname ); + } +} + +static void +xmapwpt_wr_init(const char *fname, const char *args) +{ + xmapwpt_set_style(); + + xcsv_file.xcsvfp = fopen(fname, "w"); + + if (xcsv_file.xcsvfp == NULL) { + fatal(MYNAME ": Cannot open %s for reading\n", fname ); + } +} + +static void +xmapwpt_deinit(void) +{ + if (xcsv_file.xcsvfp) + fclose(xcsv_file.xcsvfp); + + xcsv_destroy_style(); +} + +ff_vecs_t xmapwpt_vecs = { + xmapwpt_rd_init, + xmapwpt_wr_init, + xmapwpt_deinit, + xmapwpt_deinit, + xcsv_data_read, + xcsv_data_write, +}; -- 2.30.2